<

Swift 開発者向けの Flutter 同時実行性

Dart と Swift はどちらも同時プログラミングをサポートしています。 このガイドは、その方法を理解するのに役立ちます。 Dart での同時実行性の仕組みと、Swift との比較。 これを理解すると、次のようなものを作成できます。 高性能の iOS アプリ。

Apple エコシステムで開発する場合、 一部のタスクは完了するまでに時間がかかる場合があります。 これらのタスクには、大量のデータのフェッチまたは処理が含まれます。 iOS 開発者は通常、Grand Central Dispatch (GCD) を使用します。 共有スレッド プールを使用してタスクをスケジュールします。 GCD を使用すると、開発者はタスクをディスパッチキューに追加します そして GCD はそれらを実行するスレッドを決定します。

ただし、GCD はスレッドをスピンアップして、 残りの作業項目を処理します。 これは、最終的に多数のスレッドが発生する可能性があることを意味します そしてシステムがオーバーコミットになる可能性があります。 Swift では、構造化された同時実行モデルにより数が削減されました。 スレッドとコンテキストスイッチの数。 現在、各コアにはスレッドが 1 つだけあります。

Dart にはシングルスレッド実行モデルがあり、 のサポート付きIsolates、イベント ループ、および非同期コード。 アンIsolateDart の軽量スレッドの実装です。 あなたがスポーンしない限り、Isolate、Dart コードは イベント ループによって駆動されるメイン UI スレッド。 Flutterのイベントループは iOS のメインループに相当します。つまり、 メインスレッドに取り付けられたルーパー。

Dart のシングルスレッド モデルは、 すべてを実行する必要があります UI をフリーズさせるブロック操作として。 代わりに、非同期を使用してください。 Dart 言語が提供する機能、 そのようなasync/await

非同期プログラミング

非同期操作により他の操作が可能になります 完了する前に実行します。 Dart と Swift はどちらも非同期関数をサポートしています を使用してasyncawaitキーワード。 両方の場合において、async関数であることを示すマーク 非同期作業を実行し、 とawaitシステムに結果を待つように指示する 機能から。これは、Dart VM ができる必要に応じて機能を一時停止します。 非同期プログラミングの詳細については、以下を参照してください。Dart の同時実行性。

メインスレッド/分離の活用

Apple オペレーティング システムの場合、プライマリ (メインとも呼ばれます) スレッドはアプリケーションの実行を開始する場所です。 ユーザー インターフェイスのレンダリングは常にメイン スレッドで行われます。 Swift と Dart の違いの 1 つは、
Swift はタスクごとに異なるスレッドを使用する場合がありますが、 また、Swift はどのスレッドが使用されるか保証しません。 したがって、Swift で UI 更新をディスパッチするときは、 作業がメインスレッドで行われるようにする必要がある場合があります。

を取得する関数を書きたいとします。 天気を非同期的に表示し、 結果を表示します。

GCD でプロセスをメインスレッドに手動でディスパッチするには、 次のようなことを行うとよいでしょう。

まず、Weather enum:

// 1 second delay is used in mocked API call. 
extension UInt64 {
  static let oneSecond = UInt64(1_000_000_000)
} 

enum Weather: String {
    case rainy, sunny
}

次に、ビュー モデルを定義し、それをObservableObjecttype の値を返すことができるようにするWeather?。 GCD 作成を使用して、DispatchQueueに 作業をスレッドのプールに送信します

class ContentViewModel: ObservableObject {
    @Published private(set) var result: Weather?

    private let queue = DispatchQueue(label: "weather_io_queue")
    func load() {
        // Mimic 1 sec delay.
        queue.asyncAfter(deadline: .now() + 1) { [weak self] in
            DispatchQueue.main.async {
                self?.result = .sunny
            }
        }
    }
}

最後に、結果を表示します。

struct ContentView: View {
    @StateObject var viewModel = ContentViewModel()
    var body: some View {
        Text(viewModel.result?.rawValue ?? "Loading")
            .onAppear {
                viewModel.load()
        }
    }
}

最近では、Swift が導入されました俳優サポートする 共有された変更可能な状態の同期。 作業がメインスレッドで確実に実行されるようにするには、 としてマークされたビューモデルクラスを定義します。@MainActor、 とともにload()を内部的に呼び出す関数 非同期関数を使用するTask

@MainActor class ContentViewModel: ObservableObject {
  @Published private(set) var result: Weather?
  
  func load() async {
    try? await Task.sleep(nanoseconds: .oneSecond)
    self.result = .sunny
  }
}

次に、次を使用してビューモデルを状態オブジェクトとして定義します。@StateObject、 とともに8ab1c1c0-d568-404c-9​​eb0-2e74f9701c0dView Model から呼び出すことができる関数:

struct ContentView: View {
  @StateObject var viewModel = ContentViewModel()
  var body: some View {
    Text(viewModel.result?.rawValue ?? "Loading...")
      .task {
        await viewModel.load()
      }
  }
}

Dart では、デフォルトですべての作業がメイン アイソレートで実行されます。 同じ例を Dart で実装するには、次のようにします。 まず、Weather enum:

enum Weather {
  rainy,
  windy,
  sunny,
}

次に、単純なビュー モデル (SwiftUI で作成したものと同様) を定義します。 天気を取得するために。ダーツでは、Futureオブジェクトは値を表します 将来的には提供されます。あFutureスウィフトに似てるObservableObject。 この例では、ビューモデル内の関数 を返しますFuture<Weather>物体:

@immutable
class HomePageViewModel {
  const HomePageViewModel();
  Future<Weather> load() async {
    await Future.delayed(const Duration(seconds: 1));
    return Weather.sunny;
  }
}

load()この例の関数は共有します Swift コードとの類似点。 Dart 機能は次のようにマークされています。asyncなぜなら それはawaitキーワード。

さらに、次のようにマークされた Dart 関数async自動的に返しますFuture。 つまり、作成する必要はありません。Future手動でインスタンスを作成する としてマークされた関数内async

最後のステップでは、気象値を表示します。 flutterでは、FutureBuilderStreamBuilder
ウィジェットは、Future の結果を UI に表示するために使用されます。 次の例では、FutureBuilder:

class HomePage extends StatelessWidget {
  final HomePageViewModel viewModel = const HomePageViewModel();
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return CupertinoPageScaffold(
      // Feed a FutureBuilder to your widget tree.
      child: FutureBuilder<Weather>(
        // Specify the Future that you want to track.
        future: viewModel.load(),
        builder: (context, snapshot) {
          // A snapshot is of type `AsyncSnapshot` and contains the
          // state of the Future. By looking if the snapshot contains
          // an error or if the data is null, you can decide what to
          // show to the user.
          if (snapshot.hasData) {
            return Center(
              child: Text(
                snapshot.data.toString(),
              ),
            );
          } else {
            return const Center(
              child: CupertinoActivityIndicator(),
            );
          }
        },
      ),
    );
  }
}

完全な例については、以下を確認してください。非同期天気GitHub 上のファイル。

バックグラウンド スレッド/分離の活用

Flutter アプリはさまざまなマルチコア ハードウェア上で実行できます。 macOS および iOS を実行しているデバイスを含みます。 これらのアプリケーションのパフォーマンスを向上させるには、 場合によっては、異なるコアでタスクを実行する必要がある 同時に。これは特に重要です 長時間実行操作による UI レンダリングのブロックを避けるため。

Swift では、GCD を利用してグローバル キューでタスクを実行できます。 サービス品質クラス (qos) プロパティが異なります。 これはタスクの優先度を示します。

func parse(string: String, completion: @escaping ([String:Any]) -> Void) {
  // Mimic 1 sec delay.
  DispatchQueue(label: "data_processing_queue", qos: .userInitiated)
    .asyncAfter(deadline: .now() + 1) {
      let result: [String:Any] = ["foo": 123]
      completion(result)
    }
  }
}

Dart では、計算をワーカー分離にオフロードできます。 バックグラウンドワーカーと呼ばれることが多いです。 一般的なシナリオでは、単純なワーカー分離が生成され、 ワーカーが終了すると、メッセージで結果を返します。 Dart 2.19 の時点では、次を使用できます。Isolate.run()に 分離を生成して計算を実行します。

void main() async {
  // Read some data.
  final jsonData = await Isolate.run(() => jsonDecode(jsonString) as Map<String, dynamic>);`

  // Use that data.
  print('Number of JSON keys: ${jsonData.length}');
}

Flutter では、compute関数 アイソレートをスピンアップしてコールバック関数を実行するには:

final jsonData = await compute(getNumberOfKeys, jsonString);

この場合、コールバック関数はトップレベルです。 以下に示すように機能します。

Map<String, dynamic> getNumberOfKeys(String jsonString) {
 return jsonDecode(jsonString);
}

Dart について詳しくは、こちらをご覧ください。Swift 開発者として Dart を学ぶ、 Flutter の詳細については、以下をご覧ください。SwiftUI 開発者向けの FlutterまたUIKit開発者向けのFlutter